﻿using System;
using System.Collections.Generic;

namespace OpenGameKit
{
    public class Sprite
    {
        /// <summary>
        /// The id of the image file to use.
        /// </summary>
        private int ImageFile;
        /// <summary>
        /// The top left x location.
        /// </summary>
        public float X;
        /// <summary>
        /// The top left y location.
        /// </summary>
        public float Y;
        /// <summary>
        /// The last x location.
        /// </summary>
        public float PreX;
        /// <summary>
        /// The last y location.
        /// </summary>
        public float PreY;
        /// <summary>
        /// The width of the sprite.
        /// </summary>
        public int Width { get; private set; }
        /// <summary>
        /// The height of the sprite.
        /// </summary>
        public int Height { get; private set; }
        /// <summary>
        /// The angle to draw the sprite at.
        /// </summary>
        public double ImageAngle;
        /// <summary>
        /// The order to draw the sprites by.
        /// </summary>
        public int Layer;
        /// <summary>
        /// The speed the sprite moves per frame.
        /// </summary>
        public float Speed = 0;
        /// <summary>
        /// The direction the sprite moves from the speed.
        /// </summary>
        public int Direction = 0;
        /// <summary>
        /// The amount the sprite slows down per frame.
        /// </summary>
        public float Friction = 0;
        /// <summary>
        /// The amount of gravity to apply per frame.
        /// </summary>
        public float Gravity = 0;
        /// <summary>
        /// If the sprite can pass through other solids.
        /// </summary>
        public bool Solid = true;
        private float D = 0;

        public enum CollisionType
        {
            Rectangle,
            Circle,
            Tile,
        }
        /// <summary>
        /// The type of collision that is checked for.
        /// </summary>
        public CollisionType CollisionMode = CollisionType.Rectangle;
        private int CollisionRadius = 0;

        /// <summary>
        /// Called when the sprite is being rendered, allowing you to draw on the same layer as the sprite.
        /// </summary>
        public event Game.GameEventHandler OnSpriteRender;
        public event Game.GameEventHandler OnCollision;

        public Sprite()
        {
            OnSpriteRender += Sprite_OnSpriteRender;
            OnCollision += Sprite_OnCollision;
        }

        public Sprite(string image, int x, int y, int layer, Color transparent)
        {
            OnSpriteRender += Sprite_OnSpriteRender;
            OnCollision += Sprite_OnCollision;
            SetImage(image, transparent);
            X = x;
            Y = y;
            Layer = layer;
        }

        internal void Update()
        {
            CollisionRadius = Width / 2;

            if (Friction != 0) // If friction needs to be appyled, do it.
                Speed -= (Friction / 10) * Game.DeltaTime;
            if (Speed < 0) // Friction can't make things go in reverse so keep speed at a min of 0.
                Speed = 0;

            bool HadCollision = false;

            foreach (Sprite s in Game.CurrentScene.SceneSprites)
            {
                if (s != this)
                {
                    if (s.Solid == true)
                    {
                        if (this.Solid == true)
                        {
                            if (CheckCollision(s, CollisionMode) == true)
                            {
                                // TODO: Come up with a better way to do collision dection.
                                Speed = -Speed;
                                Direction = 0;
                                OnCollision(this, new GameEventArgs());
                                HadCollision = true;
                            }
                        }
                    }
                }
            }

            // If no collision was found apply gravity.
            if (HadCollision == false)
                Y += (int)Gravity * Game.DeltaTime;

            D = -Direction * ((float)Math.PI / 180);  // Get the radians from the direction angle.
        }

        internal void Render()
        {
            float MoveX = X;
            float MoveY = Y;

            MoveX += (float)Math.Cos(D) * Speed * Game.DeltaTime;  // Add the angle.
            MoveY += (float)Math.Sin(D) * Speed * Game.DeltaTime;  // Add the angle.

            Graphics.DrawImage(ImageFile, (int)MoveX, (int)MoveY, Width, Height, ImageAngle);

            // Update x and y.
            X = (int)MoveX;
            Y = (int)MoveY;

            OnSpriteRender(this, new GameEventArgs());  // This allows us to draw on the layer the sprite is on, Game.OnRender,
            // is drawn ontop of everything.

            PreX = X;
            PreY = Y;
        }

        /// <summary>
        /// Changes the image file to what you want it to be.
        /// </summary>
        /// <param name="file">The file to use.</param>
        /// <param name="transparent">The color that is drawn as transparent.</param>
        public void SetImage(string file, Color transparent)
        {
            ImageFile = Graphics.LoadImage(file, transparent);
            Width = Graphics.GetImageWidth(file);
            Height = Graphics.GetImageHeight(file);
        }

        /// <summary>
        /// Check if the sprite is colliding with another sprite.
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public bool CheckCollision(Sprite s, CollisionType ct)
        {
            switch (ct)
            {
                case CollisionType.Rectangle:

                    int left1, left2;
                    int right1, right2;
                    int top1, top2;
                    int bottom1, bottom2;

                    left1 = (int)this.X;
                    left2 = (int)s.X;
                    right1 = (int)this.X + this.Width;
                    right2 = (int)s.X + s.Width;
                    top1 = (int)this.Y;
                    top2 = (int)s.Y;
                    bottom1 = (int)this.Y + this.Height;
                    bottom2 = (int)s.Y + s.Height;

                    // Check if there is no collision.
                    if (bottom1 < top2)
                        return false;
                    if (top1 > bottom2)
                        return false;
                    if (right1 < left2)
                        return false;
                    if (left1 > right2)
                        return false;
                    break;

                case CollisionType.Circle:
                    // TODO: Test collision between box and circle.
                    if (GameMath.GetDistance(this.X, this.Y, s.X, s.Y) <= CollisionRadius)
                    {
                        return false;
                    }

                    break;
                case CollisionType.Tile:
                    break;
            }

            // If all if conditions are false there was a collision.
            return true;
        }

        public void Move(float speed, int direction)
        {
            Speed = speed;
            Direction = direction;
        }

        public void MoveTowards(int x, int y)
        {
            Direction = -(int)GameMath.LookAt(X, Y, x, y);
        }

        /// <summary>
        /// Use this before changing the image, to prevent a memmory leak.
        /// </summary>
        public void DisposeImage()
        {
            Graphics.DisposeImage(ImageFile);
        }

        public void Dispose()
        {
            Graphics.DisposeImage(ImageFile);
            GC.SuppressFinalize(this);
        }

        private void Sprite_OnSpriteRender(object sender, GameEventArgs e)
        {
        }

        private void Sprite_OnCollision(object sender, GameEventArgs e)
        {
        }
    }
}
